﻿using PGMHelper;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace HystereticBasic
{
    /// <summary>
    /// 加载路径 加载-卸载路径
    /// </summary>
    public class LoadingPath
    {
        /// <summary>
        /// 加载路径
        /// </summary>
        private MontForceDispObj loadingPath { set; get; }

        /// <summary>
        /// 卸载路径
        /// </summary>
        private MontForceDispObj unloadingPath { set; get; }

        /// <summary>
        /// 加载-卸载路径
        /// 不重复申请内存
        /// </summary>
        public ForceDispObj LoadUnloadPath
        {
            get
            {
                //初始化
                var path = new ForceDispObj(this.loadingPath.ToList());
                //获得卸载路径
                var unLoadList = this.unloadingPath.ToList();
                //遍历数据
                for (int index = 1; index < unLoadList.Count; index++)
                    path.Add(unLoadList[index]);
                //返回对象
                return path;
            }
        }

        /// <summary>
        /// 获得加载路径
        /// </summary>
        public MontForceDispObj LoadingPathObj
        {
            get
            {
                return new MontForceDispObj(this.loadingPath);
            }
        }

        /// <summary>
        /// 获得卸载路径
        /// </summary>
        public MontForceDispObj UnlaodingPathObj
        {
            get
            {
                return new MontForceDispObj(this.unloadingPath);
            }
        }

        /// <summary>
        /// 加载-卸载路径 数据点数目
        /// </summary>
        public int Count
        {
            get
            {
                //获得数据点
                var count = this.loadingPath.Count;
                //卸载数据点存在 加载-卸载存在重叠点
                if (this.isUnloadExist) count += this.loadingPath.Count - 1;
                return count;
            }
        }

        /// <summary>
        /// 是否存在卸载段
        /// </summary>
        public bool isUnloadExist
        {
            get
            {
                return this.unloadingPath.Count != 0;
            }
        }

        /// <summary>
        /// 正向加载
        /// </summary>
        public bool isPosLoading
        {
            get
            {
                return this.loadingPath.Last().Force > 0;
            }
        }

        /// <summary>
        /// 是否合法
        /// </summary>
        public bool isLegal
        {
            get
            {
                //加载路径存在数据点
                if (this.loadingPath.Count == 0) return false;
                //位移增量
                var deltaDisp = this.loadingPath.DispMaxPoint.Disp - this.loadingPath.DispMinPoint.Disp;
                //位移增量为0
                if (deltaDisp == 0) return false;
                return true;
            }

        }

        /// <summary>
        /// 峰值指向点
        /// </summary>
        public ForceDispBasicPair PeakOreintedPt
        {
            get
            {
                return this.loadingPath.Count == 0? 
                    new ForceDispBasicPair() :
                    new ForceDispBasicPair(this.loadingPath.Last());
            }
        }

        /// <summary>
        /// 峰值力点
        /// </summary>
        public ForceDispBasicPair PeakForcePt
        {
            get
            {
                return this.loadingPath.Count == 0 ?
                    new ForceDispBasicPair() :
                    new ForceDispBasicPair(this.isPosLoading? 
                    this.loadingPath.ForceMaxPoint: this.loadingPath.ForceMinPoint);
            }
        }

        /// <summary>
        /// 承载力卸载点
        /// </summary>
        public ForceDispBasicPair UnloadPt
        {
            get
            {
                //是否存在卸载段
                if (this.unloadingPath.Count == 0)
                    return new ForceDispBasicPair();
                //获得卸载终点
                var ptJ = this.unloadingPath.Last();
                var ptI = this.unloadingPath[this.unloadingPath.Count - 1];
                ForceDispBasicPair targetPt;
                //获得为零点
                if (!ptI.GetZeroForcePtInLine(ptJ, out targetPt))
                    return ptJ;
                else return targetPt;
            }
        }

        /// <summary>
        /// 虚拟位移力最大点
        /// </summary>
        public ForceDispBasicPair VirtualPeakOreintedForcePt
        {
            get
            {
                var orientPt = this.PeakOreintedPt;
                var peakForcePt = this.PeakForcePt;
                return new ForceDispBasicPair(orientPt.Disp, peakForcePt.Force);
            }
        }

        /// <summary>
        /// 虚拟卸载位移点 最后两点负刚度
        /// </summary>
        public ForceDispBasicPair VirtualUnloadPt
        {
            get
            {
                //初始化
                var pt = new ForceDispBasicPair();
                //限定数据点数目
                if (this.loadingPath.Count < 2) return pt;
                //获得点
                var ptI = this.loadingPath[this.loadingPath.Count - 2];
                var ptJ = this.loadingPath.Last();
                //非负刚度
                if (ptI.GetStiffness(ptJ) >= 0) return pt;
                //获得新的数据点
                ForceDispBasicPair interPt;
                //插值获得数据点
                if (!ptI.GetInterpolationPt(ptJ, 0, out interPt)) 
                    return pt;
                //位移过大
                return Math.Abs(interPt.Disp) > 
                    GlobalParam.VirtualUnloadPtDispFactor 
                    * Math.Abs(ptJ.Disp) ? 
                    pt : interPt;
            }
        }

        /// <summary>
        /// 峰值指向点位于24象限
        /// </summary>
        public bool isPeakOreintedInRegion24
        {
            get
            {
                var orientedPt = this.PeakOreintedPt;
                return orientedPt.Disp * orientedPt.Force <= 0;
            }
        }

        /// <summary>
        /// 指向点存在软化
        /// </summary>
        public bool isPeakOrientedPtLowerThanPeakForcePt
        {
            get
            {
                return Math.Abs(this.PeakOreintedPt.Disp - this.PeakForcePt.Disp) > GlobalParam.DispTol;
            }
        }

        /// <summary>
        /// 获得包围能量
        /// </summary>
        /// <returns></returns>
        public float GetEnergy()
        {
            return this.loadingPath.GetEnergy() + this.unloadingPath.GetEnergy();
        }

        /// <summary>
        /// 加载路径加入
        /// </summary>
        /// <returns></returns>
        public LoadPathAction GetLoadPathAction(float capacityLimit)
        {
            //加载路径存在数据点
            if (this.loadingPath.Count == 0)
                return LoadPathAction.Remove;
            //加载终点
            var loadEnd = this.loadingPath.Last();
            //力最大点
            var forceMaxPt = this.PeakForcePt;

            //存在卸载段 卸载终点的力满足要求
            if (this.unloadingPath.Count != 0 &&
                Math.Abs(this.unloadingPath.Last().Force) >
                Math.Abs(loadEnd.Force * GlobalParam.LoadPathCapacityRatio))
                return LoadPathAction.AddToNextPath;

            //终点位移落入2 4 象限
            if (this.isPeakOreintedInRegion24) return LoadPathAction.AddToNextPath;
            //终点力满足限值
            if (Math.Abs(forceMaxPt.Force) <= Math.Abs(capacityLimit))
                return LoadPathAction.AddToNextPath;
            else return LoadPathAction.AddObj;
        }

        /// <summary>
        /// 添加路径
        /// </summary>
        /// <param name="preLoadPath"></param>
        public LoadPathAction AddPreLoadPath(LoadingPath preLoadPath, float capacityLimit)
        {
            //获得最后加载点
            var preLoadEnd = preLoadPath.loadingPath.Last();
            var curLoadEnd = this.loadingPath.Last();
            //力最大点
            var preforceMaxPt = preLoadPath.PeakForcePt;
            //非同向加载
            if (curLoadEnd.Force * preLoadEnd.Force < 0)
            {
                //落入24象限
                if (preLoadPath.isPeakOreintedInRegion24) return LoadPathAction.AddObj;
                //落入13象限：且力比较大
                else if (Math.Abs(preforceMaxPt.Force) > Math.Abs(capacityLimit) * 2)
                {
                    MessageBoxExtern.Error("存在加载-卸载曲线丢失情况！");
                    return LoadPathAction.AddObj;
                }
                else return LoadPathAction.Remove;
            }
            //如果位移同向 且当前变形小于前一变形：当前存在卸载段噪点
            if (preLoadEnd.Disp * curLoadEnd.Disp > 0 && Math.Abs(preLoadEnd.Disp) > Math.Abs(curLoadEnd.Disp))
            {
                //获得卸载段
                var unLoadObj = preLoadPath.unloadingPath;
                //加入加载段
                for (int i = 1; i < this.loadingPath.Count; i++)
                    unLoadObj.Add(this.loadingPath[i]);
                //加入卸载段
                for (int i = 1; i < this.unloadingPath.Count; i++)
                    unLoadObj.Add(this.unloadingPath[i]);
                //更新卸载段
                this.unloadingPath = unLoadObj;
                //更新加载端
                this.loadingPath = preLoadPath.loadingPath;
            }
            //前一加载存在加载噪点
            else
            {
                //获得前方数据点
                var forceDispObj = new MontForceDispObj(preLoadPath.LoadUnloadPath.ToList());
                //开头结尾重叠
                for (int i = 1; i < this.loadingPath.Count; i++) forceDispObj.Add(this.loadingPath[i]);
                this.loadingPath = forceDispObj;
            }
            return LoadPathAction.Remove;
        }

        /// <summary>
        /// 设定加载路径
        /// </summary>
        /// <param name="originForceDispObj"></param>
        /// <param name="startIndex"></param>
        /// <param name="isLoad"></param>
        private void SetLoadingPath(ForceDispObj originForceDispObj, ref int startIndex, bool isLoad)
        {
            //获得对象
            var loadObj = isLoad ? this.loadingPath : this.unloadingPath;
            //定义起始点
            int index = startIndex;
            //目标点
            ForceDispBasicPair zeroForcePt;
            //遍历数据点
            for (; index < originForceDispObj.Count - 1; index++)
            {
                //当前数据点
                var curForceDisp = originForceDispObj[index];
                //下一个数据点
                var postForceDisp = originForceDispObj[index + 1];
                //计算位移增量
                var deltaDisp = postForceDisp.Disp - curForceDisp.Disp;
                //位移增量太小
                if (Math.Abs(deltaDisp) < GlobalParam.DispTol) continue;
                //是否满足条件：特殊情况 有一点位于Y=0
                bool isOK = isLoad ? (deltaDisp * postForceDisp.Force >= 0) : (deltaDisp * postForceDisp.Force <= 0);
                //满足条件
                if (isOK)
                {
                    //是否存在对象 若为加载段还需添加零点
                    if (loadObj.Count == 0)
                    {
                        //加载段关注出发点：前方有数据点 且过零点
                        if (isLoad && 
                            curForceDisp.GetZeroForcePtInLine(postForceDisp, out zeroForcePt))
                            loadObj.Add(zeroForcePt);
                        //添加当前对象
                        else loadObj.Add(curForceDisp);
                    }
                    //判断力是否同向：没有卸载段数据
                    else if(isLoad && postForceDisp.Force * curForceDisp.Force < 0)
                    {
                        //插值获得零点
                        if(curForceDisp.GetZeroForcePtInLine(postForceDisp, out zeroForcePt))
                        {
                            //构造卸载段
                            this.unloadingPath.Add(curForceDisp);
                            this.unloadingPath.Add(zeroForcePt);
                            //直接退出
                            break;
                        }
                    }
                    //添加新对象
                    loadObj.Add(postForceDisp);
                }
                //不满足条件 且不存在对象
                else if (loadObj.Count == 0) continue;
                //不满足条件 存在对象 且为加载段
                else if (isLoad) break;
                //不满足条件 存在对象 且为卸载段
                else
                {
                    //插值力为零点
                    if (curForceDisp.GetZeroForcePtInLine(postForceDisp, out zeroForcePt))
                        loadObj.Add(zeroForcePt);
                    //终止
                    break;
                }
            }
            //已经存在对象
            startIndex = index;
        }

        /// <summary>
        /// 加载路径平滑
        /// </summary>
        /// <returns></returns>
        public LoadingPath GetSmoothLoadingPath()
        {
            //构造新路径
            var loadPath = new LoadingPath();
            //加载段初始化
            loadPath.loadingPath.Add(this.loadingPath.First());
            //获得加载段的力最大值
            var forceMaxPt = this.PeakForcePt;
            //根据力最大值切为两部分
            var ObjTuple = this.loadingPath.Seperate(forceMaxPt);
            //是否正向加载
            bool isPos = this.isPosLoading;
            //上升段进行单插
            ObjTuple.Item1.Seperate(GlobalParam.SmoothCurveMeshNum).ForEach(obj =>
            loadPath.loadingPath.AddMaxStiffObj(obj, isPos));
            //上升软化段
            loadPath.loadingPath.AddMaxStiffObj(ObjTuple.Item2, isPos);
            //卸载段是否存在
            if (!this.isUnloadExist) return loadPath;
            //添加初始点
            loadPath.unloadingPath.Add(this.unloadingPath.First());
            //依次加入刚度大的点
            loadPath.unloadingPath.AddMaxStiffObj(this.unloadingPath, !isPos);
            //返回平滑对象
            return loadPath;
        }

        /// <summary>
        /// 获得拐点
        /// </summary>
        /// <returns></returns>
        public ForceDispBasicPair GetNumericalBrkPt()
        {
            //获得加载段的力最大值
            var forceMaxPt = this.PeakForcePt;
            //根据力最大值切为两部分
            var ObjTuple = this.loadingPath.Seperate(forceMaxPt);
            //上升段数值曲线
            var obj = ObjTuple.Item1.GetNumericalForceDispObj();
            //是否存在拐点
            if (obj.Count >= 3) return obj[1];
            else return obj.Last();
        }

        /// <summary>
        /// 加载-卸载路径代数化
        /// </summary>
        /// <returns></returns>
        public LoadingPath GetNumericalLoaindgPath()
        {
            //构造新路径
            var loadPath = new LoadingPath();
            //获得加载段的力最大值
            var forceMaxPt = this.PeakForcePt;
            //根据力最大值切为两部分
            var ObjTuple = this.loadingPath.Seperate(forceMaxPt);
            //添加加载硬化段
            loadPath.loadingPath.AddRange(ObjTuple.Item1.GetNumericalForceDispObj());
            //添加加载软化段
            loadPath.loadingPath.AddRange(ObjTuple.Item2.GetNumericalForceDispObj());
            //卸载段是否存在
            if (!this.isUnloadExist) return loadPath;
            //卸载段
            loadPath.unloadingPath.AddRange(this.unloadingPath.GetNumericalForceDispObj());
            //返回数值滞回路径 
            return loadPath;
        }

        /// <summary>
        /// 获得试验加载路径
        /// 通过骨架曲线加以限定
        /// </summary>
        /// <param name="skeletonObj"></param>
        /// <returns></returns>
        public LoadingPath GetPostExpLoadingPath(MontForceDispObj skeletonObj)
        {
            //指向点
            var orientedPt = this.PeakOreintedPt;
            //骨架上点
            var oirentPtOnSkeleton = skeletonObj.GetPtByDisp(orientedPt.Disp);
            var peakForcePtOnSkeleton = skeletonObj.GetPtByDisp(this.PeakForcePt.Disp);
            //骨架交点
            ForceDispBasicPair skeletonCrossPt;
            //初始化路径
            var tarloadingPath = new MontForceDispObj();
            var tarunlaodPath = new MontForceDispObj();

            //加载路径峰值点超过骨架
            if (Math.Abs(this.PeakForcePt.Force) > Math.Abs(peakForcePtOnSkeleton.Force))
            {
                //将曲线进行划分
                var seLoadPath = this.loadingPath.Seperate(this.PeakForcePt, true);
                //插值点
                if (skeletonObj.GetIntersectionPt(seLoadPath.Item1, false, out skeletonCrossPt))
                {
                    //将曲线进行划分
                    seLoadPath = this.loadingPath.Seperate(skeletonCrossPt, true);
                    //添加前半段
                    tarloadingPath.AddRange(seLoadPath.Item1);
                    //添加终点
                    tarloadingPath.Add(oirentPtOnSkeleton);
                }
                //插值不到点
                else return this;
            }
            //加载路径峰值点小于骨架
            else
            {
                //将曲线进行划分
                var seLoadPath = this.loadingPath.Seperate(this.PeakForcePt, true);
                //添加前半段
                tarloadingPath.AddRange(seLoadPath.Item1);
                //指向点位移大于峰值点
                if (this.isPeakOrientedPtLowerThanPeakForcePt)
                {
                    //计算割线刚度
                    var sStiff = this.PeakForcePt.GetStiffness(this.loadingPath.First());
                    //如果加载段存在数据点
                    if(tarloadingPath.Count > 2)
                    {
                        var subStiff = tarloadingPath[tarloadingPath.Count - 2].GetStiffness(tarloadingPath.Last());
                        if (subStiff < 0) sStiff = 0;
                        else if (subStiff < sStiff) sStiff = subStiff;
                    }
                    //计算终点力
                    var endForce = sStiff * (this.PeakOreintedPt.Disp -
                        this.PeakForcePt.Disp) + this.PeakForcePt.Force;
                    //构造终点
                    var endPt = new ForceDispBasicPair(this.PeakOreintedPt.Disp, endForce);
                    //终点力超过骨架
                    if (Math.Abs(endForce) > Math.Abs(oirentPtOnSkeleton.Force))
                        tarloadingPath.Add(oirentPtOnSkeleton);
                    //终点力小于骨架
                    else tarloadingPath.Add(endPt);
                }
            }

            //下降段
            if(this.unloadingPath.Count != 0)
            {
                //起始点不同
                if (!tarloadingPath.Last().isSamePt(this.unloadingPath.First()))
                {
                    //插入起始点
                    tarunlaodPath.Add(tarloadingPath.Last());
                    //计算力
                    var unloadForce = tarloadingPath.Last().Force * 0.5F;
                    //插值位移
                    ForceDispBasicPair unloadPt;
                    if (this.unloadingPath.GetPtByForce(unloadForce, out unloadPt))
                    {
                        //将曲线进行划分
                        var seLoadPath = this.unloadingPath.Seperate(unloadPt, true);
                        tarunlaodPath.AddRange(seLoadPath.Item2);
                    }
                    else tarunlaodPath.Add(this.unloadingPath.Last());
                }
                //起始点相同
                else tarunlaodPath = this.unloadingPath;
            }
            return new LoadingPath(tarloadingPath, tarunlaodPath);
        }

        /// <summary>
        /// 初始化
        /// </summary>
        public LoadingPath()
        {
            this.loadingPath = new MontForceDispObj();
            this.unloadingPath = new MontForceDispObj();
        }

        /// <summary>
        /// 构造加载卸载路径
        /// </summary>
        /// <param name="originForceDispObj"></param>
        /// <param name="index"></param>
        public LoadingPath(ForceDispObj originForceDispObj, ref int startIndex) : this()
        {
            //是否已经到结束点
            if (startIndex == originForceDispObj.Count - 1) return;
            //设定加载路径
            this.SetLoadingPath(originForceDispObj, ref startIndex, true);
            //是否已经到结束点
            if (startIndex == originForceDispObj.Count - 1) return;
            //卸载段存在：卸载段丢失-插值获得
            if (this.unloadingPath.Count != 0) return;
            //设定卸载路径
            this.SetLoadingPath(originForceDispObj, ref startIndex, false);
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="loadPath"></param>
        /// <param name="unloadPath"></param>
        public LoadingPath(MontForceDispObj loadPath, MontForceDispObj unloadPath)
        {
            this.loadingPath = loadPath;
            this.unloadingPath = unloadPath;
        }

    }
}
